package
{
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.BlendMode;
    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.display.Stage;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.events.MouseEvent;
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.ui.Keyboard;
    import flash.utils.getTimer;
    
    import flashx.textLayout.accessibility.TextAccImpl;

    [SWF(width="800", height="600", frameRate="60")]
    /**
     * MicrobialLife is the base for a variety of possible games.
     *
     * The basic concept is to build a little microbe simulator that can handle:
     *
     * * Mitosis
     * * Evolution of properties during mitosis
     * * Nutrient intake
     * * Toxin intake
     * * Osmosis with environment
     * * Death due to toxins or loss of mass
     *
     * And then give the ability to control the microbes to some extent allowing
     * for various types of RTS or strategy / simulation games.
     *
     * @author jjung
     */
    public class MicrobialLife extends Sprite
    {
        //---------------------------------------------------------------------
        //
        //  Variables
        //
        //---------------------------------------------------------------------
        //-----------------------------
        //  Environment
        //-----------------------------
        public var environment : Environment;

        //-----------------------------
        //  Render
        //-----------------------------
        public var screenBitmap : Bitmap;
        public var hitSprite : Sprite;
        /**
         * Scratchy is invisible and just used to dump sprites to take snapshots with
         * blendModes.
         */
        public var scratchy : Sprite;

        /**
         * Interface is visible. Used to display anything on top of the main environment.
         */
        public var interfaceSprite : Sprite;

        public var alphaChannel25 : BitmapData;
        public var alphaChannel50 : BitmapData;
        public var alphaChannel75 : BitmapData;
        public var alphaChannel90 : BitmapData;

        public var tSelected : TextField;
        public var tMicrobes : TextField;
        public var tFrameRate : TextField;

        public var tf : TextFormat;

        //-----------------------------
        //  Microbes
        //-----------------------------
        public var microbes : Array = [];
        public var microbesToSplice : Array = [];

        //-----------------------------
        //  Mitosis
        //-----------------------------
        public var mitosises : Array = [];
        public var mitosisesToSplice : Array;

        //-----------------------------
        //  Timing
        //-----------------------------
        public var lastTime : int = 0;

        //-----------------------------
        //  User Interaction
        //-----------------------------
        public var selectRect : Rectangle;

        public var nutrientsOnly : Boolean = false;
        public var toxicOnly : Boolean = false;
        public var fluidityOnly : Boolean = false;

        public var mouseDown : Boolean = false;
        public var makingSelection : Boolean = false;

        //---------------------------------------------------------------------
        //
        //  Constructor
        //
        //---------------------------------------------------------------------
        /**
         *  Constructor
         */
        public function MicrobialLife()
        {
            _instance = this;

            environment = new Environment();

            scratchy = new Sprite();
            scratchy.alpha = 0;
            addChild(scratchy);

            scratchy.x = 1000;
            scratchy.y = 1000;

            hitSprite = new Sprite();

            addChild(hitSprite);

            hitSprite.x = hitSprite.y = 0;

            hitSprite.graphics.clear();
            hitSprite.graphics.beginFill(0xFF0000, 1.0);

            hitSprite.graphics.drawRect(0, 0, 800, 600);
            hitSprite.graphics.endFill();

            hitSprite.width = 800;
            hitSprite.height = 600;

            hitSprite.addEventListener(MouseEvent.CLICK, clickHandler);
            hitSprite.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
            hitSprite.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);

            environment.generateEnvironment(50, 50, 50, 0x1, 0x1, 0xF);

            addEventListener(Event.ENTER_FRAME, enterFrameHandler);
            stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
            stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);

            screenBitmap = new Bitmap(new BitmapData(800, 600, false, 0x000000), "auto", true);

            addChild(screenBitmap);

            screenBitmap.x = 0;
            screenBitmap.y = 0;

            alphaChannel25 = new BitmapData(100, 100, true, 0x40000000);
            alphaChannel50 = new BitmapData(100, 100, true, 0x80000000);
            alphaChannel75 = new BitmapData(100, 100, true, 0xBF000000);
            alphaChannel90 = new BitmapData(100, 100, true, 0xE5000000);

            buildInterface();
        }

        private static var _instance : MicrobialLife;

        public static function getInstance() : MicrobialLife
        {
            return _instance;
        }

        //---------------------------------------------------------------------
        //
        //  Methods
        //
        //---------------------------------------------------------------------
        protected function buildInterface() : void
        {
            // Interface
            interfaceSprite = new Sprite();
            addChild(interfaceSprite);
            interfaceSprite.y = 0;
            interfaceSprite.x = 0;

            // tSelected
            tSelected = new TextField();
            tf = tSelected.getTextFormat();

            tSelected.text = "Selected: 0";
            interfaceSprite.addChild(tSelected);

            tSelected.y = 580;
            tSelected.x = 10;

            // tMicrobes
            tMicrobes = new TextField();
            tMicrobes.text = "Total: 0";
            interfaceSprite.addChild(tMicrobes);

            tMicrobes.y = 580;
            tMicrobes.x = 150;

            // tFrameRate
            tFrameRate = new TextField();
            tFrameRate.text = "0";
            interfaceSprite.addChild(tFrameRate);

            tFrameRate.y = 580;
            tFrameRate.x = 700;

            tf.color = 0xFFFFFF;
        }

        //---------------------------------------------------------------------
        //
        //  Events
        //
        //---------------------------------------------------------------------

        protected function enterFrameHandler(event : Event) : void
        {
            var numSelected : int = 0;

            tMicrobes.text = "Total: " + (microbes.length + mitosises.length);

            if (lastTime == 0)
            {
                lastTime = getTimer();
            }
			var now : int = getTimer();
            var elapsed : Number =  now - lastTime;

            elapsed = elapsed / 1000;

            if (elapsed > 0.2)
            {
                elapsed = 0.2;
            }
			else if (elapsed < 0)
			{
				elapsed = 0;
			}

            screenBitmap.bitmapData.fillRect(new Rectangle(0, 0, 800, 600), 0x000000);

            //if (!nutrientsOnly && !fluidityOnly)
                //screenBitmap.bitmapData.merge(environment.bdToxic, environment.bdToxic.rect, new Point(0, 0), 0x80, 0x00, 0x00, 0);

            //if (!toxicOnly && !fluidityOnly)
                //screenBitmap.bitmapData.merge(environment.bdNutrients, environment.bdNutrients.rect, new Point(0, 0), 0x00, 0x80, 0x00, 0);

            //if (!toxicOnly && !nutrientsOnly)
                //screenBitmap.bitmapData.merge(environment.bdFluidity, environment.bdFluidity.rect, new Point(0, 0), 0x00, 0x00, 0x80, 0);

            mitosisesToSplice = [];
            microbesToSplice = [];

			screenBitmap.bitmapData.lock();
			
            for each (var mitosis : Mitosis in mitosises)
            {
                mitosis.render();
                mitosis.update(elapsed);

                var mat : Matrix = new Matrix();
                mat.tx = mitosis.mitosisSprite.x;
                mat.ty = mitosis.mitosisSprite.y;

                screenBitmap.bitmapData.draw(mitosis.mitosisSprite, mat);
            }

			
			
            // Now rip out all the mitosises that have finished.
            for each (mitosis in mitosisesToSplice)
            {
                mitosises.splice(mitosises.indexOf(mitosis), 1);
            }

            for each (var microbe : Microbe in microbes)
            {
                if (makingSelection)
                {
                    microbe.selected = false;

                    if (microbe.x > selectRect.x && microbe.x < selectRect.x + selectRect.width && microbe.y > selectRect.y && microbe.y < selectRect.y + selectRect.height)
                    {
                        microbe.selected = true;
                    }
                }

                if (!microbe.visible)
                    continue;

                if (microbe.selected)
                {
                    numSelected++;
                }

                microbe.update(elapsed, environment);
                microbe.render();

                screenBitmap.bitmapData.copyPixels(microbe.bd, microbe.bd.rect, new Point(microbe.x - Microbe.MAX_RADIUS, microbe.y - Microbe.MAX_RADIUS), nutrientsOnly || toxicOnly || fluidityOnly ? alphaChannel25 : null, null, true);
            }

			screenBitmap.bitmapData.unlock();
			
            //tSelected.text = "Selected: " + numSelected;

            // Now rip out all the mitosises that have finished.
            for each (var microbe : Microbe in microbesToSplice)
            {
                microbes.splice(microbes.indexOf(microbe), 1);
            }

            // Draw the select rectangle
            if (makingSelection)
            {
                var sh : Shape = new Shape();
                sh.graphics.lineStyle(1, 0xFFFFFF, 0.8);
                sh.graphics.drawRect(selectRect.x, selectRect.y, selectRect.width, selectRect.height);

                screenBitmap.bitmapData.draw(sh, null, null, null, null, false);
            }

            lastTime = now;

            tFrameRate.text = Math.round(1.0 / elapsed).toString();

            tSelected.setTextFormat(tf);
            tMicrobes.setTextFormat(tf);
            tFrameRate.setTextFormat(tf);
        }

        protected function clickHandler(event : MouseEvent) : void
        {
            mouseDown = false;

            if (event.shiftKey)
            {
                for each (var microbe : Microbe in microbes)
                {
                    if (microbe.selected)
                    {
                        microbe.destination = new Point(event.stageX, event.stageY);
                        microbe.destination.x += (Math.random() * 50) - 25;
                        microbe.destination.y += (Math.random() * 50) - 25;
                    }
                }

                return;
            }

            var shouldReturn : Boolean = false;

            if (makingSelection)
            {
                makingSelection = false;

                return;
            }
            else
            {
                for each (var microbe : Microbe in microbes)
                {
                    if (microbe.selected)
                    {
                        shouldReturn = true;
                    }
                    microbe.selected = false;
                }
            }

            if (shouldReturn)
            {
                return;
            }

            if (event.shiftKey)
            {
                environment.subtractNutrients(event.stageX, event.stageY, 30, 0.02);
            }
            else
            {
                var microbe : Microbe = new Microbe();
                microbe.x = event.stageX;
                microbe.y = event.stageY;

                microbes.push(microbe);
            }
        }

        protected function keyDownHandler(event : KeyboardEvent) : void
        {
            if (event.keyCode == 65)
            {
                nutrientsOnly = true;
            }
            else if (event.keyCode == 68)
            {
                fluidityOnly = true;
            }
            else if (event.keyCode == 83)
            {
                toxicOnly = true;
            }
        }

        protected function keyUpHandler(event : KeyboardEvent) : void
        {
            nutrientsOnly = false;
            toxicOnly = false;
            fluidityOnly = false;
        }

        protected function mouseDownHandler(event : MouseEvent) : void
        {

            mouseDown = true;

            selectRect = new Rectangle(event.stageX, event.stageY, 1, 1);
        }

        protected function mouseMoveHandler(event : MouseEvent) : void
        {
            if (mouseDown)
            {
                makingSelection = true;

                selectRect.width = event.stageX - selectRect.x;
                selectRect.height = event.stageY - selectRect.y;
            }
        }
    }
}